import { type MarkSpec, type NodeSpec, Schema } from "@rivet/model";

import type { AnyConfig, Extensions } from "../types.ts";
import { callOrReturn } from "../utilities/callOrReturn.ts";
import { isEmptyObject } from "../utilities/isEmptyObject.ts";
import { getAttributesFromExtensions } from "./getAttributesFromExtensions.ts";
import { getExtensionField } from "./getExtensionField.ts";
import { splitExtensions } from "./splitExtensions.ts";
import { MarkConfig, NodeConfig } from "@valuation/core";

function cleanUpSchemaItem<T>(data: T) {
  return Object.fromEntries(
    // @ts-ignore:
    Object.entries(data).filter(([key, value]) => {
      // @ts-ignore:
      if (key === "attrs" && isEmptyObject(value)) {
        return false;
      }

      return value !== null && value !== undefined;
    }),
  ) as T;
}
/*
解析扩展的属性并生成Schema
解析Node节点的 content marks group attrs属性
解析Mark标记的 attrs属性
*/
export function getSchemaByResolvedExtensions(extensions: Extensions): Schema {
  const allAttributes = getAttributesFromExtensions(extensions);
  const { nodeExtensions, markExtensions } = splitExtensions(extensions);
  const topNode = nodeExtensions.find((extension) =>
    getExtensionField(extension, "topNode")
  )?.name;

  const nodes = Object.fromEntries(nodeExtensions.map((extension) => {
    const extensionAttributes = allAttributes.filter((attribute) =>
      attribute.type === extension.name
    );
    const context = {
      name: extension.name,
      options: extension.options,
      storage: extension.storage,
    };

    const extraNodeFields = extensions.reduce((fields, e) => {
      const extendNodeSchema = getExtensionField<AnyConfig["extendNodeSchema"]>(
        e,
        "extendNodeSchema",
        context,
      );

      return {
        ...fields,
        // @ts-ignore: 这里返回的就是node 类型
        ...(extendNodeSchema ? extendNodeSchema(extension) : {}),
      };
    }, {});

    const schema: NodeSpec = cleanUpSchemaItem({
      ...extraNodeFields,
      content: callOrReturn(
        getExtensionField<NodeConfig["content"]>(extension, "content", context),
      ),
      marks: callOrReturn(
        getExtensionField<NodeConfig["marks"]>(extension, "marks", context),
      ),
      group: callOrReturn(
        getExtensionField<NodeConfig["group"]>(extension, "group", context),
      ),
      attrs: Object.fromEntries(
        extensionAttributes.map((extensionAttribute) => {
          return [extensionAttribute.name, {
            default: extensionAttribute?.attribute?.default,
          }];
        }),
      ),
    });

    return [extension.name, schema];
  }));

  const marks = Object.fromEntries(markExtensions.map((extension) => {
    const extensionAttributes = allAttributes.filter((attribute) =>
      attribute.type === extension.name
    );
    const context = {
      name: extension.name,
      options: extension.options,
      storage: extension.storage,
    };

    const extraMarkFields = extensions.reduce((fields, e) => {
      const extendMarkSchema = getExtensionField<AnyConfig["extendMarkSchema"]>(
        e,
        "extendMarkSchema",
        context,
      );

      return {
        ...fields,
        ...(extendMarkSchema ? extendMarkSchema(extension) : {}),
      };
    }, {});

    const schema: MarkSpec = cleanUpSchemaItem({
      ...extraMarkFields,
      group: callOrReturn(
        getExtensionField<MarkConfig["group"]>(extension, "group", context),
      ),
      attrs: Object.fromEntries(
        extensionAttributes.map((extensionAttribute) => {
          return [extensionAttribute.name, {
            default: extensionAttribute?.attribute?.default,
          }];
        }),
      ),
    });
    return [extension.name, schema];
  }));

  return new Schema({
    topNode,
    nodes,
    marks,
  });
}
